package com.smartfactory.sail.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smartfactory.sail.entity.*;
import com.smartfactory.sail.entity.enums.ContractTypeEnum;
import com.smartfactory.sail.entity.enums.OrderItemStatusEnum;
import com.smartfactory.sail.exception.PlanInvalidException;
import com.smartfactory.sail.mapper.PlanMapper;
import com.smartfactory.sail.service.*;
import com.smartfactory.sail.vo.plan.PlanDetailResp;
import com.smartfactory.sail.vo.plan.PlanReq;
import com.smartfactory.sail.vo.plan.PlanResp;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 王巨壁
 * @since 2019-09-05
 */
@Service
@Transactional
public class PlanServiceImpl extends ServiceImpl<PlanMapper, Plan> implements IPlanService {

    private IOrderItemService iOrderItemService;
    private IItemService iItemService;
    private IOrderService iOrderService;
    private PlanMapper planMapper;
    private IContractService iContractService;
    private IPurchaseService iPurchaseService;
    private IStorageRecordService iStorageRecordService;

    public PlanServiceImpl(IOrderItemService iOrderItemService, IItemService iItemService, IOrderService iOrderService, PlanMapper planMapper, IContractService iContractService, IPurchaseService iPurchaseService, IStorageRecordService iStorageRecordService) {
        this.iOrderItemService = iOrderItemService;
        this.iItemService = iItemService;
        this.iOrderService = iOrderService;
        this.planMapper = planMapper;
        this.iContractService = iContractService;
        this.iPurchaseService = iPurchaseService;
        this.iStorageRecordService = iStorageRecordService;
    }

    @Override
    public List<PlanDetailResp> create(Long id, Integer hours, String startDate, boolean isUrgentConfirm) {
        if (null == id || null == hours || null == startDate)
            throw new PlanInvalidException("参数无效");
        LocalDate parseStartDate = LocalDate.parse(startDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        if (parseStartDate.isBefore(LocalDate.now()))
            throw new PlanInvalidException("排班起始时间小于当前时间");
        OrderItem orderItem = iOrderItemService.getById(id);
        OrderItemStatusEnum status = orderItem.getStatus();
        if (!status.equals(OrderItemStatusEnum.DPB) && !status.equals(OrderItemStatusEnum.YLZ))
            throw new IllegalArgumentException("当前订单产品不可排班");
        if (status.equals(OrderItemStatusEnum.YLZ))
            cancelPlan(orderItem);

        Item item = iItemService.getById(orderItem.getItemId());
        Order order = iOrderService.getById(orderItem.getOrderId());
        Contract contract = iContractService.getById(order.getContractId());

        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getItemId, orderItem.getItemId());
        queryWrapper.eq(Plan::getOrderId, orderItem.getOrderId());
        List<Plan> plans = planMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(plans))
            throw new PlanInvalidException("该产品已排班");

        int hourlyCapacity = item.getHourlyCapacity();//时产能
        int size = orderItem.getSize();//订单数量
        int totalHours = (int) Math.ceil(size * 1.0 / hourlyCapacity);

        if (orderItem.getContractType().equals(ContractTypeEnum.PT) && isUrgentConfirm)
            isUrgentConfirm = false;
        Dept dept = iItemService.getDeptByItemId(item.getId());
        Plan plan = new Plan();
        plan.setText(item.getName() + " " + item.getSpecification());
        plan.setDeptId(dept.getId());
        plan.setDeptName(dept.getDeptName());
        plan.setOrderId(orderItem.getOrderId());
        plan.setOrderCode(order.getOrderCode());
        plan.setOrderMode(order.getOrderMode().getCode());
        plan.setContractId(contract.getId());
        plan.setContractCode(contract.getContractCode());
        plan.setContractType(contract.getContractType());
        plan.setUnderOrderId(order.getUnderOrderId());
        plan.setItemId(item.getId());
        plan.setItemCode(item.getCode());
        plan.setItemName(item.getName());
        plan.setItemInfo(item.getInfo());
        plan.setItemSpecification(item.getSpecification());
        plan.setHourlyCapacity(item.getHourlyCapacity());
        plan.setDeliveryDate(order.getDeliveryDate());
        plan.setItemSize(orderItem.getSize());
        //plan.setStatus();

        recursion(parseStartDate, totalHours, hours, plan, isUrgentConfirm ? 11 : 8);

        orderItem.setStatus(OrderItemStatusEnum.YLZ);
        iOrderItemService.updateById(orderItem);

        return preview(orderItem, dept.getId());
    }

    @Override
    public List<PlanDetailResp> preview(Long id) {
        OrderItem orderItem = iOrderItemService.getById(id);
        Dept dept = iItemService.getDeptByItemId(orderItem.getItemId());
        return preview(orderItem, dept.getId());
    }

    private List<PlanDetailResp> preview(OrderItem orderItem, long deptId) {
        if (!orderItem.getStatus().equals(OrderItemStatusEnum.YLZ))
            throw new IllegalArgumentException("当前排班不可预览");
        List<Plan> deptPlans = planMapper.selectPlanedAndPreviewOrderItem(deptId);
        Map<String, Integer> otherHours = deptPlans.stream().collect(Collectors.groupingBy(Plan::getPlanDate, Collectors.summingInt(Plan::getWorkHours)));

        List<Plan> plansOfProduct = plansOfProduct(orderItem.getItemId(), orderItem.getOrderId());

        return planDetail(otherHours, plansOfProduct);
    }

    /**
     * @param deptId
     * @param startDate 开始日期  返回数据包含开始日期
     * @param days      包含开始时间  一个几天
     * @return
     */
    @Override
    public List<PlanDetailResp> listPlanedOrderItem(long deptId, String startDate, Integer days) {
        if (days < 1)
            throw new IllegalArgumentException("显示天数不能小于1");
        List<Plan> deptPlans = planMapper.selectPlanedAndPreviewOrderItem(deptId);
        Map<String, Integer> hours = deptPlans.stream().collect(Collectors.groupingBy(Plan::getPlanDate, Collectors.summingInt(Plan::getWorkHours)));
        List<Plan> plans = planMapper.selectPlanedOrderItem(deptId);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String today = LocalDate.now().format(formatter);
        Map<String, List<Plan>> groups = plans.stream().collect(Collectors.groupingBy(Plan::getPlanDate, TreeMap::new, Collectors.toList()));
        Map<String, List<Plan>> treePlans = plans.stream()
                .collect(Collectors.groupingBy(p -> p.getOrderId() + "_" + p.getItemId(), TreeMap::new, Collectors.toList()));

        List<PlanDetailResp> planDetailResps = new ArrayList<>();
        for (int i = 0; i < days; i++) {
            List<Plan> planList = groups.get(startDate);
            PlanDetailResp planDetailResp = new PlanDetailResp();
            planDetailResp.setDate(startDate);
            Integer integer = hours.get(startDate);
            planDetailResp.setRemainWorkHours(11 - (null == integer ? 0 : integer));
            List<PlanResp> planResps = treePlans.values().stream().map(pls -> {
                Plan p = pls.get(0);
                Long orderId = p.getOrderId();
                Long itemId = p.getItemId();
                //待排时间
                int remainTime = pls.stream().filter(remainTimePlan -> remainTimePlan.getPlanDate().compareTo(today) > 0).mapToInt(Plan::getWorkHours).sum();
                PlanResp planResp = new PlanResp();
                Plan curPlan = new Plan();
                BeanUtils.copyProperties(p, curPlan);
                curPlan.setWorkHours(0);
                if (null != planList) {
                    Optional<Plan> any = planList.stream().filter(filterPlan -> filterPlan.getOrderId().equals(orderId) && filterPlan.getItemId().equals(itemId)).findAny();
                    if (any.isPresent()) {
                        curPlan = any.get();
                    }
                }
                BeanUtils.copyProperties(curPlan, planResp);
                planResp.setRemainTime(remainTime);
                return planResp;
            }).collect(Collectors.toList());

            planDetailResp.setPlanResps(planResps);
            planDetailResps.add(planDetailResp);
            startDate = LocalDate.parse(startDate, formatter).plusDays(1).format(formatter);
        }

        return planDetailResps;
    }

    private List<PlanDetailResp> planDetail(Map<String, Integer> hours, List<Plan> plans) {
        String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        Map<String, List<Plan>> groups = plans.stream().collect(Collectors.groupingBy(Plan::getPlanDate, TreeMap::new, Collectors.toList()));
        Map<String, List<Plan>> treePlans = plans.stream()
                .collect(Collectors.groupingBy(p -> p.getOrderId() + "_" + p.getItemId(), TreeMap::new, Collectors.toList()));
        return groups.values().stream().map(value -> {
            Plan plan = value.get(0);
            String planDate = plan.getPlanDate();
            PlanDetailResp planDetailResp = new PlanDetailResp();
            planDetailResp.setDate(planDate);
            planDetailResp.setRemainWorkHours(11 - hours.get(planDate));

            ArrayList<PlanResp> planResps = new ArrayList<>();

            treePlans.forEach((key, planList) -> {
                Plan p = planList.get(0);
                Long orderId = p.getOrderId();
                Long itemId = p.getItemId();
                //待排时间
                int remainTime = planList.stream().filter(remainTimePlan -> remainTimePlan.getPlanDate().compareTo(today) > 0).mapToInt(Plan::getWorkHours).sum();

//                //结束时间
//                Optional<Plan> max = planList.stream().max(Comparator.comparing(Plan::getPlanDate));
//
//                //开始时间
//                Optional<Plan> min = planList.stream().min(Comparator.comparing(Plan::getPlanDate));

                Optional<Plan> any = value.stream().filter(filterPlan -> filterPlan.getOrderId().equals(orderId) && filterPlan.getItemId().equals(itemId)).findAny();

                PlanResp planResp = new PlanResp();
                if (any.isPresent()) {
                    Plan curPlan = any.get();
                    BeanUtils.copyProperties(curPlan, planResp);
                } else {
                    BeanUtils.copyProperties(p, planResp);
                    planResp.setWorkHours(0);
                }
                planResp.setRemainTime(remainTime);
//                max.ifPresent(maxPlan -> planResp.setEndDate(maxPlan.getPlanDate()));
//                min.ifPresent(minPlan -> planResp.setStartDate(minPlan.getPlanDate()));
                planResps.add(planResp);
            });

            planDetailResp.setPlanResps(planResps);
            return planDetailResp;
        }).collect(Collectors.toList());
    }

    @Override
    public List<Plan> planOne(Long orderId, Long itemId, Integer hours, String date) {
        Item item = iItemService.getById(itemId);
        Order order = iOrderService.getById(orderId);
        Contract contract = iContractService.getById(order.getContractId());
        OrderItem orderItem = iOrderItemService.findByOrderIdAndItemId(itemId, orderId);
        Dept dept = iItemService.getDeptByItemId(item.getId());
        Plan plan = new Plan();
        plan.setText(item.getName() + " " + item.getSpecification());
        plan.setDeptId(dept.getId());
        plan.setDeptName(dept.getDeptName());
        plan.setOrderId(orderItem.getOrderId());
        plan.setOrderCode(order.getOrderCode());
        plan.setOrderMode(order.getOrderMode().getCode());
        plan.setContractId(contract.getId());
        plan.setContractCode(contract.getContractCode());
        plan.setContractType(contract.getContractType());
        plan.setUnderOrderId(order.getUnderOrderId());
        plan.setItemId(item.getId());
        plan.setItemCode(item.getCode());
        plan.setItemName(item.getName());
        plan.setItemInfo(item.getInfo());
        plan.setItemSpecification(item.getSpecification());
        plan.setHourlyCapacity(item.getHourlyCapacity());
        plan.setDeliveryDate(order.getDeliveryDate());
        plan.setItemSize(orderItem.getSize());

        recursion(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd")), hours, hours, plan, 11);

        return plansOfProduct(itemId, orderId);
    }

    @Override
    public int cancelPlan(long orderId, long itemId) {
        OrderItem orderItem = iOrderItemService.findByOrderIdAndItemId(itemId, orderId);
        return cancelPlan(orderItem);
    }

    private int cancelPlan(OrderItem orderItem) {
        if (!orderItem.getStatus().equals(OrderItemStatusEnum.YLZ))
            throw new IllegalArgumentException("非预览中排班不可取消");
        orderItem.setStatus(OrderItemStatusEnum.DPB);
        iOrderItemService.updateById(orderItem);
        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getOrderId, orderItem.getOrderId());
        queryWrapper.eq(Plan::getItemId, orderItem.getItemId());
        return planMapper.delete(queryWrapper);
    }

    @Override
    public void confirmPlan(long orderId, long itemId) {
        OrderItem orderItem = iOrderItemService.findByOrderIdAndItemId(itemId, orderId);
        if (!orderItem.getStatus().equals(OrderItemStatusEnum.YLZ))
            throw new IllegalArgumentException("非预览中排班不可确认");
        //异步调用
        iPurchaseService.orderPurChaseHandle(orderId, itemId);
    }

    @Override
    public List<PlanDetailResp> rePlan(PlanReq planReq) {
        long itemId = planReq.getItemId();
        long orderId = planReq.getOrderId();
        int hours = planReq.getHours();
        String date = planReq.getDate();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getOrderId, orderId);
        queryWrapper.eq(Plan::getItemId, itemId);
        queryWrapper.eq(Plan::getPlanDate, date);
        Plan plan = planMapper.selectOne(queryWrapper);

        int workHours;
        List<Plan> plans = plansOfProduct(itemId, orderId);
        if (null == plan) {
            if (hours <= 0)
                throw new IllegalArgumentException("新增的排班小时数必须大于0");
            planOne(orderId, itemId, hours, date);
            workHours = 0;
        } else
            workHours = plan.getWorkHours();

        if (hours < workHours) {
            String maxDate = planMapper.findMaxDate(orderId, itemId);
            if (maxDate.equals(date))
                maxDate = LocalDate.parse(maxDate, formatter).plusDays(1).format(formatter);
            if (hours < 0)
                hours = 0;
            if (hours == 0) {
                planMapper.delete(queryWrapper);
            } else {
                plan.setWorkHours(hours);
                planMapper.updateById(plan);
            }
            int planHours = workHours - hours;
            planOne(orderId, itemId, planHours, maxDate);
        } else if (hours > workHours) {
            if (null != plan)
                planMapper.rePlan(orderId, itemId, date, hours);
            int extra = hours - workHours;
            for (int i = plans.size() - 1; i >= 0; i--) {
                if (extra == 0)
                    break;
                Plan p = plans.get(i);
                String planDate = p.getPlanDate();
                if (date.equals(planDate))
                    continue;
                int wh = p.getWorkHours();
                if (wh > extra) {
                    planMapper.rePlan(orderId, itemId, planDate, wh - extra);
                    break;
                } else {
                    LambdaQueryWrapper<Plan> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(Plan::getOrderId, orderId);
                    wrapper.eq(Plan::getItemId, itemId);
                    wrapper.eq(Plan::getPlanDate, planDate);
                    planMapper.delete(wrapper);
                    extra = extra - wh;
                }
            }
        }

        Dept dept = iItemService.getDeptByItemId(itemId);
        List<Plan> deptPlans = planMapper.selectPlanedAndPreviewOrderItem(dept.getId());
        Map<String, Integer> otherHours = deptPlans.stream().collect(Collectors.groupingBy(Plan::getPlanDate, Collectors.summingInt(Plan::getWorkHours)));

        List<Plan> plansOfProduct = plansOfProduct(itemId, orderId);

        return planDetail(otherHours, plansOfProduct);
    }

    @Override
    public List<Plan> plansOfProduct(long itemId, long orderId) {
        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getItemId, itemId);
        queryWrapper.eq(Plan::getOrderId, orderId);
        return planMapper.selectList(queryWrapper).stream().sorted(Comparator.comparing(Plan::getPlanDate)).collect(Collectors.toList());
    }

    @Scheduled(cron = "0 30 0 1/1 * ?")
    public void planCheck() {
        String format = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getPlanDate, format);
        List<Plan> currentPlans = planMapper.selectList(queryWrapper);
        String start = format + " 00:00:00";
        String end = format + " 23:59:59";
        currentPlans.forEach(plan -> {
            int num = plan.getWorkHours() * plan.getHourlyCapacity();
            Long itemId = plan.getItemId();
            Long orderId = plan.getOrderId();
            LambdaQueryWrapper<StorageRecord> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StorageRecord::getItemId, itemId);
            wrapper.eq(StorageRecord::getOrderId, orderId);
            wrapper.between(StorageRecord::getCreateTime, start, end);
            List<StorageRecord> storageRecords = iStorageRecordService.list(wrapper);
            int sum = storageRecords.stream().mapToInt(StorageRecord::getSize).sum();
            if (num > sum) {
                int remain = num - sum;
                int h = (int) Math.ceil(remain * 1.0 / plan.getHourlyCapacity());
                plan.setWorkHours(plan.getWorkHours() - h);
                planMapper.updateById(plan);
                recursion(LocalDate.now(), h, h, plan, plan.getContractType().equals(ContractTypeEnum.JJ) ? 11 : 8);
            }
        });
    }

    private void recursion(LocalDate date, int totalHours, int hoursOfDay, Plan plan, int maxHours) {
        if (totalHours <= 0 || hoursOfDay <= 0 || null == plan)
            return;
        String dateString = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getPlanDate, dateString);
        queryWrapper.eq(Plan::getDeptId, plan.getDeptId());
        List<Plan> currentPlans = planMapper.selectList(queryWrapper);

        int sum;
        //计算已排班的时间  单位：小时
//        if (isUrgent) {
//            for (Plan currentPlan : currentPlans) {
//                if (currentPlan.getContractType().equals(ContractTypeEnum.PT)) {
//                    planMapper.deleteById(currentPlan.getId());
//                    //普通的往后延
//                    recursion(date.plusDays(1), currentPlan.getWorkHours(), currentPlan.getWorkHours(), currentPlan, false, maxHours);
//                } else if (currentPlan.getContractType().equals(ContractTypeEnum.JJ)) {
//                    sum += currentPlan.getWorkHours();
//                }
//            }
//            maxHours = 11;
//        }
//        else
//            if (!CollectionUtils.isEmpty(currentPlans) && currentPlans.get(0).getContractType().equals(ContractTypeEnum.JJ))
//            sum = maxHours;
//        else
        sum = currentPlans.stream().mapToInt(Plan::getWorkHours).sum();

        if (sum >= maxHours)
            recursion(date.plusDays(1), totalHours, hoursOfDay, plan, maxHours);
        else {
            int remain = maxHours - sum;
            //如果剩余的工作时间比每天工作时间还长 那还是以每天工作时间为准
            if (remain > hoursOfDay)
                remain = hoursOfDay;
            //如果剩余时间比总时间还长，那以总剩余时间为准
            if (remain > totalHours)
                remain = totalHours;
            List<Plan> collect = currentPlans.stream().filter(cPlan -> cPlan.getOrderId().equals(plan.getOrderId()) && cPlan.getItemId().equals(plan.getItemId())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                Plan p = collect.get(0);
                p.setWorkHours(p.getWorkHours() + remain);
                planMapper.updateById(p);
            } else {
                Plan one = new Plan();
                BeanUtils.copyProperties(plan, one);
                one.setId(null);
                one.setPlanDate(dateString);
                one.setWorkHours(remain);
                planMapper.insert(one);
            }
            totalHours -= remain;
            recursion(date.plusDays(1), totalHours, hoursOfDay, plan, maxHours);
        }
    }
}
